package com.zmx.core.executor;

import com.zmx.core.exception.JobException;
import com.zmx.core.handler.MethodJobHandler;
import com.zmx.core.handler.annotation.MallJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @Description: 基于spring的任务执行类
 * @ClassName: JobSpringExecutor
 * @Author zhaomxr
 * @Date 2021-12-17 11:07
 */
@Slf4j
public class JobSpringExecutor extends JobExecutor implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {

    private static ApplicationContext applicationContext;

    @Override
    public void afterSingletonsInstantiated() {
        initJobHandlerMethodRepository();
        // super start
        try {
            super.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        JobSpringExecutor.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void destroy() throws Exception {

    }

    private void initJobHandlerMethodRepository() {
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

        // 遍历所有加载的bean
        for (String beanName : beanDefinitionNames) {
            Object bean = applicationContext.getBean(beanName);

            // 取带有@MallJob注解的方法，构建map
            Map<Method, MallJob> annotatedMethods = null;
            if (bean != null) {
                try {
                    annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(), new MethodIntrospector.MetadataLookup<MallJob>() {
                        @Override
                        public MallJob inspect(Method method) {
                            return AnnotatedElementUtils.findMergedAnnotation(method, MallJob.class);
                        }
                    });
                } catch (Exception e) {
                    log.error("mall-job initJobHandler error bean is {}", beanName);
                }
            }

            if (annotatedMethods == null || annotatedMethods.size() == 0) {
                continue;
            }

            //遍历所有method，组装MethodJobHandler到jobHandlerRepository
            for (Map.Entry<Method, MallJob> map : annotatedMethods.entrySet()) {
                Method method = map.getKey();
                MallJob mallJob = map.getValue();

                if (mallJob == null) {
                    continue;
                }
                String name = mallJob.value();
                if (StringUtils.isEmpty(name)) {
                    throw new JobException("the job bean " + bean.getClass().toString() + " of method " + method.getName() + " no value");
                }
                if(loadJobHandler(name) != null){
                    throw new JobException("the job bean " + bean.getClass().toString() + " of method " + method.getName() + " has exit");
                }

                method.setAccessible(true);

                // init and destory
                Method initMethod = null;
                Method destroyMethod = null;

                if (!StringUtils.isEmpty(mallJob.initMethod())) {
                    try {
                        initMethod = bean.getClass().getDeclaredMethod(mallJob.initMethod());
                        //禁止权限检查，极大提高性能
                        initMethod.setAccessible(true);
                    } catch (NoSuchMethodException e) {
                        throw new JobException("the job bean " + bean.getClass().toString() + " of method " + mallJob.initMethod() + " no value");
                    }
                }
                if (!StringUtils.isEmpty(mallJob.destoryMethod())) {
                    try {
                        destroyMethod = bean.getClass().getDeclaredMethod(mallJob.destoryMethod());
                        //禁止权限检查，极大提高性能
                        destroyMethod.setAccessible(true);
                    } catch (NoSuchMethodException e) {
                        throw new JobException("the job bean " + bean.getClass().toString() + " of method " + mallJob.destoryMethod() + " no value");
                    }
                }

                //注册当前任务到jobHandlerRepository处理类中
                registJobHandler(name, new MethodJobHandler(bean, method, initMethod, destroyMethod));
            }
        }
    }
}
